package com.jwater.core.net.group.lock;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @description:
 * @author: jnner
 * @createDate: 2021/2/5
 * @version: 1.0
 */
public class GroupLock {
    protected static Logger logger = LoggerFactory.getLogger(GroupLock.class);

    public static final long LOCK_TIMEOUT = 60000;
    private volatile Map<String, LockInfo> locks = new ConcurrentHashMap<>(16);
    private volatile Map<String, Lock> controls = new ConcurrentHashMap<>(16);

    private synchronized Lock getControl(String key) {
        Lock controlLock = controls.get(key);
        if (controlLock == null) {
            controlLock = new ReentrantLock();
            controls.put(key, controlLock);
        }
        return controlLock;
    }

    /**
     * 分布式锁本地控制逻辑
     *
     * @param key
     * @param memberId
     * @param threadId
     */
    public void lock(String key, int memberId, long threadId) {
        Lock controlLock = getControl(key);
        controlLock.lock();
        try {
            LockInfo lock = locks.get(key);
            if (lock != null) {
                if (System.currentTimeMillis() - lock.getUpdateTime() > LOCK_TIMEOUT) {
                    // 锁超时，直接移除，防止之前的程序由于各种网络异常未解锁
                    locks.remove(key);
                } else {
                    if (memberId == lock.getMemberId() && threadId == lock.getThreadId()) {
                        // 支持可重入锁，计数器加1
                        lock.getCount().incrementAndGet();
                        return;
                    }
                }
            }
            // 等待锁释放，等待最大超时时间，设置超时时间是为了防止之前的程序由于各种网络异常未解锁
            long waitTime = 0;
            while ((lock = locks.get(key)) != null && waitTime < LOCK_TIMEOUT) {
                try {
                    TimeUnit.MILLISECONDS.sleep(10);
                    waitTime += 10;
                } catch (InterruptedException e) {
                    logger.error(e.toString(), e);
                }
            }
            lock = new LockInfo();
            lock.setMemberId(memberId);
            lock.setThreadId(threadId);
            lock.setUpdateTime(System.currentTimeMillis());
            lock.getCount().incrementAndGet();
            // 存储锁
            locks.put(key, lock);
        } finally {
            controlLock.unlock();
        }
    }

    /**
     * 解锁
     *
     * @param key
     */
    public void unlock(String key) {
        LockInfo lock = locks.get(key);
        if (lock != null) {
            lock.getCount().decrementAndGet();
            if (lock.getCount().get() <= 0) {
                locks.remove(key);
                controls.remove(key);
            }
        }
    }

}
